package pers.xiaojun.boot.web.core.handler;

import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingRequestValueException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import pers.xiaojun.boot.common.exception.BusinessException;
import pers.xiaojun.boot.common.pojo.CommonResult;

import java.nio.file.AccessDeniedException;
import java.sql.SQLException;
import java.sql.SQLNonTransientConnectionException;
import java.util.Arrays;

import static pers.xiaojun.boot.common.constants.GlobalBusinessCodeConstants.*;

/**
 * 描述
 *
 * @author xiaojun
 * @since 2025-10-04
 */
@RestControllerAdvice
@AllArgsConstructor
@Slf4j
public class GlobalExceptionHandler {


    @ExceptionHandler(MethodArgumentNotValidException.class)
    private CommonResult<?> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
        log.warn("[methodArgumentNotValidExceptionHandler]", ex);
        return CommonResult.error(BAD_REQUEST.getCode(), ex.getBindingResult().getFieldError().getDefaultMessage());
    }

    /**
     * 处理 @RequestParam 注解请求参数缺失异常
     */
    @ExceptionHandler(MissingRequestValueException.class)
    private CommonResult<?> missingRequestValueExceptionHandler(MissingRequestValueException ex) {
        log.warn("[missingRequestValueExceptionHandler]", ex);
        return CommonResult.error(BAD_REQUEST);
    }

    /**
     * 处理 SpringMVC 请求地址不存在
     */
    @ExceptionHandler(NoResourceFoundException.class)
    private CommonResult<?> noResourceFoundExceptionHandler(HttpServletRequest req, NoResourceFoundException ex) {
        log.warn("[noResourceFoundExceptionHandler]", ex);
        return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getResourcePath()));
    }

    /**
     * 处理 SpringMVC 请求方法不正确
     * <p>
     * 例如说，A 接口的方法为 GET 方式，结果请求方法为 POST 方式，导致不匹配
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public CommonResult<?> httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException ex) {
        log.warn("[httpRequestMethodNotSupportedExceptionHandler]", ex);
        return CommonResult.error(METHOD_NOT_ALLOWED.getCode(), String.format("请求方法不正确:%s", ex.getMessage()));
    }

    /**
     * 处理 SpringMVC 请求的 Content-Type 不正确
     * <p>
     * 例如说，A 接口的 Content-Type 为 application/json，结果请求的 Content-Type 为 application/octet-stream，导致不匹配
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public CommonResult<?> httpMediaTypeNotSupportedExceptionHandler(HttpMediaTypeNotSupportedException ex) {
        log.warn("[httpMediaTypeNotSupportedExceptionHandler]", ex);
        return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求类型不正确:%s", ex.getMessage()));
    }

    /**
     * 处理 Spring Security 权限不足的异常
     * <p>
     * 来源是，使用 @PreAuthorize 注解，AOP 进行权限拦截
     */
    @ExceptionHandler(value = AuthorizationDeniedException.class)
    public CommonResult<?> accessDeniedExceptionHandler(HttpServletRequest req, AuthorizationDeniedException ex) {
        log.warn("[accessDeniedExceptionHandler][userId(?) 无法访问 url({})]", req.getRequestURL(), ex);
        return CommonResult.error(FORBIDDEN);
    }

    /**
     * 处理数据库异常
     */
    @ExceptionHandler(value = SQLException.class)
    public CommonResult<?> SQLExceptionHandler(SQLException ex) {
        if (ex instanceof SQLNonTransientConnectionException) {
            log.error("[SQLNonTransientConnectionException] 数据库连接超时");
        } else {
            log.error("MYSQL异常");
        }
        return CommonResult.error(INTERNAL_SERVER_ERROR);
    }

    /**
     * 业务异常处理
     */
    @ExceptionHandler(BusinessException.class)
    public CommonResult<?> businessExceptionHandler(BusinessException ex) {
        log.warn(Arrays.toString(ex.getStackTrace()));
        log.warn("[BusinessException] {}", ex.getMessage());
        return CommonResult.error(ex.getCode(), ex.getMessage());
    }

    /**
     * 系统异常处理（兜底）
     */
    @ExceptionHandler(Throwable.class)
    public CommonResult<?> systemExceptionHandler(HttpServletRequest req, Throwable ex) {
        log.error("[systemExceptionHandler]", ex);
        return CommonResult.error(INTERNAL_SERVER_ERROR);
    }


}
